#include "Vector3.h"

#include <Rz/Math.h>


namespace Rz { namespace Graphics { namespace Math {

using namespace Rz::Math;

Vector3::Vector3():
	X(0.0f),
	Y(0.0f),
	Z(0.0f)
{

}

// TODO: RZ_ASSERT
Vector3::Vector3(std::initializer_list<f32> v)
	: X(*v.begin())
	, Y(*(v.begin() + 1))
	, Z(*(v.begin() + 2))
{

}

Vector3::Vector3(f32 x, f32 y, f32 z):
	X(x),
	Y(y),
	Z(z)
{

}

Vector3::Vector3(const f32* ptr):
	X(ptr[0]),
	Y(ptr[1]),
	Z(ptr[2])
{

}

Vector3::Vector3(const Vector3& vec):
	X(vec.X),
	Y(vec.Y),
	Z(vec.Z)
{

}

void Vector3::Set(f32 x, f32 y, f32 z)
{
	X = x;
	Y = y;
	Z = z;
}

f32 Vector3::Length() const
{
	return Math::Sqrt(X * X + Y * Y + Z * Z);
}

Vector3& Vector3::Normalize()
{
	f32 len = Length();

	if (len)
	{
		len = 1.0f / len;
		X *= len;
		Y *= len;
		Z *= len;
	}

	return *this;
}

Vector3 Vector3::Normalized() const
{
	Vector3 result = (*this);
	result.Normalize();
	return result;
}

Vector3& Vector3::Negate()
{
	X = -X;
	Y = -Y;
	Z = -Z;

	return *this;
}

Vector3 Vector3::Negated()
{
	Vector3 result = (*this);
	result.Negate();
	return result;
}

f32 Vector3::Dot(const Vector3& u, const Vector3& v)
{
	return u.X * v.X + u.Y * v.Y + u.Z * v.Z;
}

f32 Vector3::Distance(const Vector3& u, const Vector3& v)
{
	return Math::Sqrt((u.X - v.X) * (u.X - v.X) + (u.Y - v.Y) * (u.Y - v.Y) + (u.Z - v.Z) * (u.Z - v.Z));
}

Vector3 Vector3::Cross(const Vector3& u, const Vector3& v)
{
	return Vector3(u.Y * v.Z - u.Z * v.Y, u.Z * v.X - u.X * v.Z, u.X * v.Y - u.Y * v.X);
}

f32 Vector3::operator[] (s32 index) const
{
	return Cell[index];
}

f32& Vector3::operator[] (s32 index)
{
	return Cell[index];
}

Vector3 Vector3::operator + (const Vector3& vec) const
{
	Vector3 result = (*this);
	result.X += vec.X;
	result.Y += vec.Y;
	result.Z += vec.Z;
	return result;
}

Vector3 Vector3::operator - (const Vector3& vec) const
{
	Vector3 result = (*this);
	result.X -= vec.X;
	result.Y -= vec.Y;
	result.Z -= vec.Z;
	return result;
}

Vector3 Vector3::operator * (const Vector3& vec) const
{
	Vector3 result = (*this);
	result.X *= vec.X;
	result.Y *= vec.Y;
	result.Z *= vec.Z;
	return result;
}

Vector3 Vector3::operator / (const Vector3& vec) const
{
	Vector3 result = (*this);
	result.X /= vec.X;
	result.Y /= vec.Y;
	result.Z /= vec.Z;
	return result;
}

Vector3 Vector3::operator + (f32 v) const
{
	Vector3 result = (*this);
	result.X += v;
	result.Y += v;
	result.Z += v;
	return result;
}

Vector3 Vector3::operator - (f32 v) const
{
	Vector3 result = (*this);
	result.X -= v;
	result.Y -= v;
	result.Z -= v;
	return result;
}

Vector3 Vector3::operator * (f32 v) const
{
	Vector3 result = (*this);
	result.X *= v;
	result.Y *= v;
	result.Z *= v;
	return result;
}

Vector3 Vector3::operator / (f32 v) const
{
	Vector3 result = (*this);
	result.X /= v;
	result.Y /= v;
	result.Z /= v;
	return result;
}

Vector3& Vector3::operator += (const Vector3& v)
{
	*this = *this + v;
	return *this;
}

Vector3& Vector3::operator -= (const Vector3& v)
{
	*this = *this - v;
	return *this;
}
Vector3& Vector3::operator *= (const Vector3& v)
{
	*this = *this * v;
	return *this;
}

Vector3& Vector3::operator /= (const Vector3& v)
{
	*this = *this / v;
	return *this;
}

Vector3& Vector3::operator += (f32 v)
{
	*this = *this + v;
	return *this;
}

Vector3& Vector3::operator -= (f32 v)
{
	*this = *this - v;
	return *this;
}

Vector3& Vector3::operator *= (f32 v)
{
	*this = *this * v;
	return *this;
}

Vector3& Vector3::operator /= (f32 v)
{
	*this = *this / v;
	return *this;
}

Vector3 Vector3::operator - () const
{
	Vector3 result = *this;

	result.X = -result.X;
	result.Y = -result.Y;
	result.Z = -result.Z;

	return result;
}

Vector3 operator + (f32 v, const Vector3& u)
{
	return u + v;
}

Vector3 operator - (f32 v, const Vector3& u) 
{
	return u - v;
}

Vector3 operator * (f32 v, const Vector3& u)
{
	return u * v;
}

Vector3 operator / (f32 v, const Vector3& u)
{
	return u / v;
}

//////////////////////////////////////////////////////////////////////////
// IHumanReadable
String Vector3::ToString() const
{
	return String::Format("(%.3f, %.3f, %.3f)", X, Y, Z);
}

//////////////////////////////////////////////////////////////////////////
// IEquatable
bool Vector3::Equals(const Vector3& r) const
{
	return X == r.X && Y == r.Y && Z == r.Z;
}

bool Vector3::operator == (const Vector3& r) const
{
	return  Equals(r);
}

bool Vector3::operator != (const Vector3& r) const
{
	return !Equals(r);
}

//////////////////////////////////////////////////////////////////////////
// constants

const Vector3 Vector3::Zero    = Vector3(0.0f, 0.0f, 0.0f);
const Vector3 Vector3::One     = Vector3(1.0f, 1.0f, 1.0f);
const Vector3 Vector3::NormalX = Vector3(1.0f, 0.0f, 0.0f);
const Vector3 Vector3::NormalY = Vector3(0.0f, 1.0f, 0.0f);
const Vector3 Vector3::NormalZ = Vector3(0.0f, 0.0f, 1.0f);

} } }
